feat(ide-sync): add Kimi Code as supported IDE target - #644
Conversation
Adds Kimi Code (https://www.kimi-cli.com) to the IDE sync pipeline as a new target with format `kimi-skill`. Kimi consumes per-skill directories shaped as `<skill-id>/SKILL.md` and discovers them at the project Git root, so the transformer differs from the flat layouts used by Claude Code, Codex, Gemini, Cursor, and Antigravity. Changes - transformers/kimi.js: new transformer producing SKILL.md with YAML frontmatter, Activation Protocol directive, persona, Star Commands table, and the full raw agent definition. Includes: - skillId normalization (avoids `aios-aios-master` double prefix) - support for `preferredActivationAlias` - safe rendering of YAML array items that parse as objects (`- CRITICAL: x` was previously serialized as `[object Object]`) - index.js: register transformer, add `kimi` default target with `fallbackSources: ['.codex/agents']`, and write nested `<skill-id>/SKILL.md` paths during sync and validation expectation. - validator.js: walk markdown recursively so nested Kimi layouts are matched against expected files instead of being flagged as orphaned. - tests/ide-sync/kimi-transformer.test.js: covers skillId rules, preferredActivationAlias, [object Object] regression, Activation Protocol presence, and nested layout filenames (5/5 passing). Discovery contract Kimi resolves project root to the nearest `.git` ancestor, so the generated `.kimi/skills/` should live at that level (or be reachable via symlink). No changes to other IDE targets or behaviors. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
@dinis-prog is attempting to deploy a commit to the SINKRA - AIOX Team on Vercel. A member of the Team first needs to authorize it. |
WalkthroughThis pull request introduces support for a new Kimi skill IDE format. The changes add a Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.Comment |
There was a problem hiding this comment.
Welcome to aiox-core! Thanks for your first pull request.
What happens next?
- Automated checks will run on your PR
- A maintainer will review your changes
- Once approved, we'll merge your contribution!
PR Checklist:
- Tests pass (
npm test) - Linting passes (
npm run lint) - Commit messages follow Conventional Commits
Thanks for contributing!
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
tests/ide-sync/kimi-transformer.test.js (1)
56-62: ⚡ Quick winAdd explicit coverage for
preferred_activation_alias(snake_case).
getPreferredActivationId()supports both alias field variants; adding this test guards backward-compatibility behavior directly.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/ide-sync/kimi-transformer.test.js` around lines 56 - 62, Add a new test mirroring the existing preferredActivationAlias case but passing the snake_case field name to ensure backward compatibility: call kimi.getSkillId with agent: { preferred_activation_alias: 'davi-ribas' } and assert the result equals 'aios-davi-ribas'; this exercises getPreferredActivationId()'s support for both preferredActivationAlias and preferred_activation_alias..aiox-core/infrastructure/scripts/ide-sync/index.js (1)
34-34: ⚡ Quick winUse the repo’s absolute-import style for the new transformer require.
The newly added import is relative, which diverges from the project rule for JS/TS imports.
As per coding guidelines
**/*.{js,jsx,ts,tsx}: "Use absolute imports instead of relative imports in all code".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.aiox-core/infrastructure/scripts/ide-sync/index.js at line 34, The require for the new transformer uses a relative path ("./transformers/kimi"); change it to the repository's absolute-import style while keeping the const name kimiTransformer (i.e., replace the './transformers/kimi' module specifier with the project's absolute module specifier that points to the same transformer), so the import follows the repo rule of using absolute imports for JS/TS files.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.aiox-core/infrastructure/scripts/ide-sync/transformers/kimi.js:
- Around line 205-214: The loop over yaml.design_rules can throw when a value is
null because typeof null === 'object'; update the guard inside the for...of that
iterates over dr (const dr = yaml.design_rules) to ensure value is not null
before accessing value.rule (e.g., check value !== null && typeof value ===
'object' && value.rule). Make this change where the code currently checks typeof
value === 'object' and uses value.rule so null cases are safely skipped or
handled.
- Around line 302-316: getSkillId/getPreferredActivationId/getDirname currently
allow activation aliases/ids that contain "/", "\" or ".." which can enable path
traversal; sanitize the preferred activation alias (and fallback agentData.id)
by normalizing and rejecting or stripping path characters before returning a
dirname: validate the value returned by getPreferredActivationId(), remove any
path separators and parent-traversal sequences, enforce a safe character set
(e.g. alphanumerics, dash, underscore) or replace invalid chars with '-', and if
the result is empty revert to a safe fallback or throw; update getSkillId to
return the sanitized token (still prefixed with "aios-" when needed) and ensure
getDirname simply returns that sanitized skill id.
---
Nitpick comments:
In @.aiox-core/infrastructure/scripts/ide-sync/index.js:
- Line 34: The require for the new transformer uses a relative path
("./transformers/kimi"); change it to the repository's absolute-import style
while keeping the const name kimiTransformer (i.e., replace the
'./transformers/kimi' module specifier with the project's absolute module
specifier that points to the same transformer), so the import follows the repo
rule of using absolute imports for JS/TS files.
In `@tests/ide-sync/kimi-transformer.test.js`:
- Around line 56-62: Add a new test mirroring the existing
preferredActivationAlias case but passing the snake_case field name to ensure
backward compatibility: call kimi.getSkillId with agent: {
preferred_activation_alias: 'davi-ribas' } and assert the result equals
'aios-davi-ribas'; this exercises getPreferredActivationId()'s support for both
preferredActivationAlias and preferred_activation_alias.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: be53592a-04a7-4497-9337-f61259f37489
📒 Files selected for processing (4)
.aiox-core/infrastructure/scripts/ide-sync/index.js.aiox-core/infrastructure/scripts/ide-sync/transformers/kimi.js.aiox-core/infrastructure/scripts/ide-sync/validator.jstests/ide-sync/kimi-transformer.test.js
| if (yaml.design_rules) { | ||
| const dr = yaml.design_rules; | ||
| let text = '### Design Rules\n\n'; | ||
| for (const [key, value] of Object.entries(dr)) { | ||
| if (typeof value === 'object' && value.rule) { | ||
| text += `- **${key}:** ${value.rule}\n`; | ||
| } else if (typeof value === 'string') { | ||
| text += `- **${key}:** ${value}\n`; | ||
| } | ||
| } |
There was a problem hiding this comment.
Guard against null values in design_rules.
At Line 209, typeof value === 'object' is true for null, so value.rule can throw. A null check avoids transform-time failures on partially populated YAML.
✅ Minimal fix
- if (typeof value === 'object' && value.rule) {
+ if (value && typeof value === 'object' && value.rule) {
text += `- **${key}:** ${value.rule}\n`;
} else if (typeof value === 'string') {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.aiox-core/infrastructure/scripts/ide-sync/transformers/kimi.js around lines
205 - 214, The loop over yaml.design_rules can throw when a value is null
because typeof null === 'object'; update the guard inside the for...of that
iterates over dr (const dr = yaml.design_rules) to ensure value is not null
before accessing value.rule (e.g., check value !== null && typeof value ===
'object' && value.rule). Make this change where the code currently checks typeof
value === 'object' and uses value.rule so null cases are safely skipped or
handled.
| function getPreferredActivationId(agentData) { | ||
| const agent = agentData.agent || {}; | ||
| const preferred = agent.preferredActivationAlias || agent.preferred_activation_alias; | ||
| return String(preferred || agentData.id || '').trim(); | ||
| } | ||
|
|
||
| function getSkillId(agentData) { | ||
| const id = getPreferredActivationId(agentData); | ||
| if (id.startsWith('aios-')) return id; | ||
| return `aios-${id}`; | ||
| } | ||
|
|
||
| function getDirname(agentData) { | ||
| return getSkillId(agentData); | ||
| } |
There was a problem hiding this comment.
Sanitize skillId to block path traversal via activation alias/id.
getSkillId() currently accepts /, \, and .. patterns. Since getDirname() feeds directly into path.join(...) during writes (in .aiox-core/infrastructure/scripts/ide-sync/index.js, Line 198), a crafted alias can escape .kimi/skills.
🔒 Suggested hardening patch
+function sanitizePathSegment(value) {
+ return String(value || '')
+ .trim()
+ .replace(/[\\/]+/g, '-')
+ .replace(/\.\.+/g, '-')
+ .replace(/[^a-zA-Z0-9:_-]/g, '-')
+ .replace(/-+/g, '-')
+ .replace(/^-|-$/g, '');
+}
+
function getSkillId(agentData) {
- const id = getPreferredActivationId(agentData);
- if (id.startsWith('aios-')) return id;
- return `aios-${id}`;
+ const safeId = sanitizePathSegment(getPreferredActivationId(agentData));
+ const prefixed = safeId.startsWith('aios-') ? safeId : `aios-${safeId}`;
+ return prefixed || 'aios-unknown';
}As per coding guidelines **/*.js: "Look for potential security vulnerabilities."
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| function getPreferredActivationId(agentData) { | |
| const agent = agentData.agent || {}; | |
| const preferred = agent.preferredActivationAlias || agent.preferred_activation_alias; | |
| return String(preferred || agentData.id || '').trim(); | |
| } | |
| function getSkillId(agentData) { | |
| const id = getPreferredActivationId(agentData); | |
| if (id.startsWith('aios-')) return id; | |
| return `aios-${id}`; | |
| } | |
| function getDirname(agentData) { | |
| return getSkillId(agentData); | |
| } | |
| function getPreferredActivationId(agentData) { | |
| const agent = agentData.agent || {}; | |
| const preferred = agent.preferredActivationAlias || agent.preferred_activation_alias; | |
| return String(preferred || agentData.id || '').trim(); | |
| } | |
| function sanitizePathSegment(value) { | |
| return String(value || '') | |
| .trim() | |
| .replace(/[\\/]+/g, '-') | |
| .replace(/\.\.+/g, '-') | |
| .replace(/[^a-zA-Z0-9:_-]/g, '-') | |
| .replace(/-+/g, '-') | |
| .replace(/^-|-$/g, ''); | |
| } | |
| function getSkillId(agentData) { | |
| const safeId = sanitizePathSegment(getPreferredActivationId(agentData)); | |
| const prefixed = safeId.startsWith('aios-') ? safeId : `aios-${safeId}`; | |
| return prefixed || 'aios-unknown'; | |
| } | |
| function getDirname(agentData) { | |
| return getSkillId(agentData); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.aiox-core/infrastructure/scripts/ide-sync/transformers/kimi.js around lines
302 - 316, getSkillId/getPreferredActivationId/getDirname currently allow
activation aliases/ids that contain "/", "\" or ".." which can enable path
traversal; sanitize the preferred activation alias (and fallback agentData.id)
by normalizing and rejecting or stripping path characters before returning a
dirname: validate the value returned by getPreferredActivationId(), remove any
path separators and parent-traversal sequences, enforce a safe character set
(e.g. alphanumerics, dash, underscore) or replace invalid chars with '-', and if
the result is empty revert to a safe fallback or throw; update getSkillId to
return the sanitized token (still prefixed with "aios-" when needed) and ensure
getDirname simply returns that sanitized skill id.
|
Superseded by #683. I ported the useful Kimi IDE-sync implementation onto current main, resolved the conflicts, addressed the actionable CodeRabbit feedback, generated the |
Summary
Adds Kimi Code (https://www.kimi-cli.com) to the IDE sync pipeline as a new target with format
kimi-skill. Kimi consumes per-skill directories shaped as<skill-id>/SKILL.mdand discovers them at the project Git root, so the transformer differs from the flat layouts used by Claude Code, Codex, Gemini, Cursor, and Antigravity.Changes
transformers/kimi.js(new): producesSKILL.mdwith YAML frontmatter,## Activation Protocoldirective, persona, Star Commands table, and the full raw agent definition. Includes:aios-aios-masterdouble-prefix bug)preferredActivationAlias- CRITICAL: xwas previously serialized as[object Object], regression covered by test)index.js: register transformer, addkimidefault target withfallbackSources: ['.codex/agents'], and emit nested<skill-id>/SKILL.mdpaths during sync and validation expectation building.validator.js: walk markdown recursively so nested Kimi layouts are matched against expected files instead of being flagged as orphaned.tests/ide-sync/kimi-transformer.test.js(new): 5 tests covering skillId rules,preferredActivationAlias, the[object Object]regression, Activation Protocol presence, and nested layout filenames.Discovery contract
Kimi resolves project root to the nearest
.gitancestor, so generated.kimi/skills/should live at that level (or be reachable via symlink).Test plan
npx jest tests/ide-sync/kimi-transformer.test.js— 5/5 PASSnpm run sync:ide -- --ide kimi --dry-runproduces nested skill pathsnpm run sync:ide:check -- --ide kimipasses after a fresh syncNotes
/skill:aios-*activation, MCP integration, and hooks.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Tests